package com.fxu.framework.core.holder;

import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.env.Environment;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition;
import org.springframework.web.servlet.mvc.condition.RequestMethodsRequestCondition;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

/**
 * Spring 锚点类
 * @author fangxu
 * @version 1.0.0
 */
@Slf4j
@Component
public class SpringHolder implements ApplicationContextAware {
	private static ApplicationContext applicationContext;

	@Override
	public void setApplicationContext(@Nullable ApplicationContext applicationContext) {
		if (SpringHolder.applicationContext == null) {
			SpringHolder.applicationContext = applicationContext;
		}
	}

	/**
	 * 获取applicationContext
	 * @return ApplicationContext
	 */
	public static ApplicationContext getApplicationContext() {
		return applicationContext;
	}

	/**
	 * 通过name获取 Bean
	 * @param <T> Bean类型
	 * @param name Bean名称
	 * @return Bean
	 */
	@SuppressWarnings("unchecked")
	public static <T> T getBean(String name) {
		return (T) applicationContext.getBean(name);
	}

	/**
	 * 通过class获取Bean
	 * @param <T> Bean类型
	 * @param clazz Bean类
	 * @return Bean对象
	 */
	public static <T> T getBean(Class<T> clazz) {
		return applicationContext.getBean(clazz);
	}

	/**
	 * 通过name,以及Clazz返回指定的Bean
	 * @param <T> bean类型
	 * @param name Bean名称
	 * @param clazz bean类型
	 * @return Bean对象
	 */
	public static <T> T getBean(String name, Class<T> clazz) {
		return applicationContext.getBean(name, clazz);
	}

	/**
	 * 判断name的注入是否存在
	 * @param name Bean的名字
	 * @return 存在true,不存在false
	 */
	public static boolean hasBean(String name) {
		return applicationContext.containsBean(name);
	}

	/**
	 * 通过class获取Bean
	 * @param name Bean名字
	 * @return Bean对象
	 */
	public static Class<?> getType(String name) {
		return applicationContext.getType(name);
	}

	/**
	 * 获取spring.profiles.active
	 * @return 当前的环境
	 */
	public static String[] getProfiles() {
		return getApplicationContext() == null ? null : getApplicationContext().getEnvironment().getActiveProfiles();
	}

	/**
	 * 获取spring.profiles.active
	 * @return 当前的环境
	 */
	public static String getProfile() {
		return getProfile("filter,pub");
	}

	/**
	 * 获取spring.profiles.active
	 * @param exclude 排除名称集合
	 * @return 当前的环境
	 */
	public static String getProfile(String exclude) {
		String[] profiles = getProfiles();
		if (profiles == null) return "dev";
		List<String> excludes = StrUtil.split(StrUtil.isEmpty(exclude) ? "filter,pub" : exclude, ",");
		// log.debug("profiles={},excludes={}", Arrays.toString(profiles), excludes);
		return Arrays.stream(profiles).filter(profile -> excludes.stream()
				.noneMatch(v -> v.equalsIgnoreCase(profile))).findFirst().orElse("");
	}

	/**
	 * 打印所有Bean注入
	 * @param clazz 启动类
	 */
	public static void loggerBeans(Class<?> clazz) {
		log.info("===============================打印注入类================================");
		if (getApplicationContext() != null) {
			String start = clazz == null ? "" : clazz.getPackage().getName();
			String[] beans = getApplicationContext().getBeanDefinitionNames();
			for (String bean : beans) {
				String className = getApplicationContext().getBean(bean).getClass().getName();
				if (className.startsWith(start) || bean.startsWith(start) || bean.endsWith("Mapper")) {
					//  && className.startsWith("com.sun.proxy")
					log.info(bean + " of Type :: " + getApplicationContext().getBean(bean).getClass());
				}
			}
		} else {
			log.error("getApplicationContext() is null! 请在启动类增加 @Import(SpringHolder.class)");
		}
		log.info("===============================打印对外接口================================");
		RequestMappingHandlerMapping mapping = getBean("requestMappingHandlerMapping");
		for (Entry<RequestMappingInfo, HandlerMethod> m : mapping.getHandlerMethods().entrySet()) {
			Map<String, String> map = new HashMap<>();
			RequestMappingInfo info = m.getKey();
			HandlerMethod method = m.getValue();
			PatternsRequestCondition p = info.getPatternsCondition();
			if (p != null) {
				for (String url : p.getPatterns()) {
					map.put("url", url);
				}
			}
			map.put("c", method.getBean().toString()); // 方法名
			RequestMethodsRequestCondition methodsCondition = info.getMethodsCondition();
			for (RequestMethod requestMethod : methodsCondition.getMethods()) {
				map.put("type", requestMethod.toString());
			}
			log.info(map.toString());
		}
		log.info("=================================打印结束==================================");
		Environment env = getApplicationContext().getEnvironment();
		String ip = null;
		try {
			ip = InetAddress.getLocalHost().getHostAddress();
		} catch (UnknownHostException e) {
			log.info("获取本地IP错误!", e);
		}
		String port = env.getProperty("server.port");
		String path = env.getProperty("server.servlet.context-path");
		//log.info("--------------------------------------------------------------------------");
		log.info("Application use fxu-framework is running! Access URLs:");
		//log.info("Local: http://localhost:" + port + path + "/");
		//log.info("External: http://" + ip + ":" + port + path + "/");
		log.info("Swagger文档: http://" + ip + ":" + port + path + "/doc.html");
		log.info("fxu-framework文档: https://ljpt.yuyesoft.com/docs/");
		log.info("--------------------------------------------------------------------------");
	}
}
